<?php
# SimpleXMLRPC for PHP4
# License : GPL
# Author  : Steven Apostolou
# Email   : account@dds.nl

# Version 0.7

////
//! Class that creates ab XMLRPC message
// PRE  : 
// POST : 
class SimpleXMLRPCMakeMessage {

    var $xmlrpcString;
    var $tagStack;




    ////
    //! Constructor
    // PRE  : 
    // POST : Class members are initialized
    function SimpleXMLRPCMakeMessage () {
        $this->xmlrpcString = '';
        $this->tagStack     = Array ();
    }





    ////
    //! Function to unshift array onto the tagStack
    // PRE  : 
    // POST : Tag is unshifted onto the tagStack
    function AddTag ($tagName, $tagCData = '') {
        $this->xmlrpcString .= "\n<$tagName>$tagCData";
        array_unshift ($this->tagStack, $tagName);
    }





    ////
    //! Function to shift array off the beginning of the tagStack
    // PRE  : 
    // POST : Tag is shifted off the tagstack
    function CloseTag () {
        $closeTag = array_shift ($this->tagStack);
        $this->xmlrpcString .= "</$closeTag>\n";
    }





    ////
    //! Function to unshift array on the tagStack
    // PRE  : The name of the tag and possible CDATA
    // POST : Array is unshifted on the tagStack
    function GetMessageXML () {
        foreach ($this->tagStack as $closeTag) {
            $this->CloseTag ();
        }

        return $this->xmlrpcString;
    }





    ////
    //! Function that 'creates' an XMLRPC message
    // PRE  : The methodName of the method that the XMLRPC message wants to call
    // POST : XMLRPC message header is set
    function CreateXMLRPCRequestMessage ($method) {
        if ( !preg_match ('/^[\w\.:\/]+$/', $method) ) {
            trigger_error ("Illegal values in the method name: '$method'.\n", E_USER_ERROR);
        }

        $methodName = utf8_encode($method);

        $this->xmlrpcString = '';

        $this->AddTag ('methodCall');
        $this->AddTag ('methodName', $methodName);
        $this->CloseTag ();
        $this->AddTag ('params');
    }





    ////
    //! Function that 'creates' an XMLRPC message
    // PRE  : The methodName of the method that the XMLRPC message wants to call
    // POST : XMLRPC message header is set
    function CreateXMLRPCResponseMessage () {
        $this->xmlrpcString = '';

        $this->AddTag ('methodResponse');
        $this->AddTag ('params');
    }





    ////
    //! Function to set an array in the XMLPRC message
    // PRE  : 
    // POST : Passed array is put in XMLRPC message
    function AddArray (&$array) {
        $this->AddParamNode ();
        $this->AddValueNode ();

        $this->_ProcessArray ($array);

        $this->CloseTag ();
        $this->CloseTag ();
    }





    ////
    //! Function to set an array in the XMLPRC message
    // PRE  : 
    // POST : Passed array is put in XMLRPC message
    function AddValue ($value, $type = '') {
        $this->AddParamNode ();
        $this->AddValueNode ();

        # In case no type is been given
        if ($type == '') {
            switch ( gettype ($value) ) {
                case 'boolean' : $type = 'boolean'; break;
                case 'integer' : $type = 'i4'     ; break;
                case 'double'  : $type = 'double' ; break;
                case 'string'  : $type = 'string' ; break;
                case 'base64'  : $type = 'base64' ; break;
            }
        }

        $this->AddSingleValueType ($value, $type);

        $this->CloseTag ();
        $this->CloseTag ();
    }





    ////
    //! Function that walks through an array to produce an XMLRPC call
    // PRE  : 
    // POST : XMLRPC call
    function _ProcessArray ($array, $new_array = 0, $is_sub = 0, $in_struct = 0) {
        if ( is_array ($array) ) {
            # Check if we're dealing with an 'normal' array or an associative array
            # Because an array is actually a map in PHP all arrays are associative
            # To make a distinction between a normally called 'normal' array and an
            # associative array I check if all the keys are of type integer and are 
            # increasing in value with 1. If not then it's assumed that we're dealing
            # with an associative array
            # THE 'NORMAL' ARRAY HAS TO BE FRESH (NOT MODIFIED) OTHERWISE THE INDICES
            # MIGHT NOT BE INCREASING WITH 1
            $is_hash = false;
            $arrayKeys = array_keys ($array);

            for ($i = 0; $i < count ($arrayKeys); $i++ ) {
                if ( gettype ($arrayKeys[$i]) == 'string' or $i != $arrayKeys[$i]) {
                    $is_hash = true;
                    break 1;
                }
            }
            # ##################################################################### #

            # Shift first element of array
            reset($array);
            $temp = each($array);

            # Got nothing back -> toedeledoki!
            if (!is_array($temp)) return 1;

            # Get the values of the current shift
            $key   = $temp['key'];
            $value = $temp['value'];

            # Delete the shift so we can go to the next record in the array
            unset ( $array[$temp['key']] );
        } else {
            return 1;
        }


        # Check if the array is an associative array
        if ($is_hash) {
            if ($is_sub) {
                if (!$new_array) {
                    # Add structure (within value node)
                    $this->AddStructNode ();
                    $this->AddStructMemberNode ($key);
                    $this->AddValueNode ();
                    $new_array = 1;

                    if ( gettype ($value) == 'array' ) {
                        $this->_ProcessArray ($value, 0, 1, 1);
                    } else {
                        $this->AddSingleValue ($value);
                        $this->CloseTag ();
                        $this->CloseTag ();
                    }

                    $this->_ProcessArray ($array, $new_array, $is_sub, 1);

                    if ($in_struct) {
                        $this->CloseTag ();
                    }

                    $this->CloseTag ();
                    $this->CloseTag ();
                } else {
                    # Add member mode in structure
                    $this->AddStructMemberNode ($key);
                    $this->AddValueNode ();

                    if ( gettype ($value) == 'array' ) {
                        $this->_ProcessArray ($value, 0, 1, 1);
                    } else {
                        $this->AddSingleValue ($value);
                        $this->CloseTag ();
                        $this->CloseTag ();
                    }

                    $this->_ProcessArray ($array, $new_array, $is_sub, 1);
                }
            } else {
                # Add new structure
                $this->AddStructNode ();
                $this->AddStructMemberNode ($key);
                $this->AddValueNode ();

                if ( gettype ($value) == 'array' ) {
                    $this->_ProcessArray ($value, 0, 1, 1);
                } else {
                    $this->AddSingleValue ($value);
                    $this->CloseTag ();
                    $this->CloseTag ();
                }

                $new_array = 1;
                $is_sub = 1;
                $this->_ProcessArray ($array, $new_array, $is_sub, 1);

                if ($in_struct) {
                    $this->CloseTag ();
                }

                $this->CloseTag ();
            }
        } else {
            # Check for the first element of the array
            if ($is_sub) {
                if (!$new_array) {
                    # Add new array (within value node)
                    $this->AddArrayNode ();
                    $this->AddValueNode ();
                    $new_array = 1;

                    if ( gettype ($value) == 'array' ) {
                        $this->_ProcessArray ($value, 0, 1, 0);
                    } else {
                        $this->AddSingleValue ($value);
                        $this->CloseTag (); # close tag value
                    }

                    $this->RenewArray (&$array);
                    $this->_ProcessArray ($array, $new_array, $is_sub, 0);

                    if ($in_struct) {
                        $this->CloseTag ();
                    }

                    $this->CloseTag ();
                    $this->CloseTag ();
                    $this->CloseTag ();
                } else {
                    $this->AddValueNode ();

                    # Add value node
                    if ( gettype ($value) == 'array' ) {
                        $this->_ProcessArray ($value, 0, 1, 0);
                    } else {
                        $this->AddSingleValue ($value);
                        $this->CloseTag ();
                    }

                    $this->RenewArray (&$array);
                    $this->_ProcessArray ($array, $new_array, $is_sub, 0);
                }
            } else {
                # Add new array (with value node)
                $this->AddArrayNode ();
                $this->AddValueNode ();

                if ( gettype ($value) == 'array' ) {
                    $this->_ProcessArray ($value, 0, 1, 0);
                } else {
                    $this->AddSingleValue ($value);
                    $this->CloseTag ();
                }

                $new_array = 1;
                $is_sub = 1;
                $this->RenewArray (&$array);
                $this->_ProcessArray ($array, $new_array, $is_sub, 0);

                if ($in_struct) {
                    $this->CloseTag ();
                }

                $this->CloseTag ();
                $this->CloseTag ();
            }
        }

        return 1;
    }





    ////
    //! Function to 'line-up' the records in an array
    // PRE  : 
    // POST : Array has nice order without 'a hole'
    function RenewArray (&$array) {
            $tempArray = array ();
            foreach ($array as $value) {
                $tempArray[] = $value;
            }

            $array = $tempArray;
            $tempArray = NULL;
    }





    ////
    //! Function to add a param-tag
    // PRE  : 
    // POST : Param-tag is child of given $params_node
    function AddParamNode () {
        $this->AddTag ('param');
    }





    ////
    //! Function to add an array-tag
    // PRE  : 
    // POST : Array and data tag are added to the xml output string
    function AddArrayNode () {
        $this->AddTag ('array');
        $this->AddTag ('data');
    }





    ////
    //! Function to add a struct-tag
    // PRE  : 
    // POST : Struct tag is added to the xml output string
    function AddStructNode () {
        $this->AddTag ('struct');
    }





    ////
    //! Function to add a member-tag with given name
    // PRE  : 
    // POST : Member and name tag are added to the xml output string
    function AddStructMemberNode ($name) {
        $this->AddTag ('member');
        $this->AddTag ('name', $name);
        $this->CloseTag ();
    }





    ////
    //! Function to add a value-tag
    // PRE  : 
    // POST : Value tag is added to the xml output string
    function AddValueNode () {
        $this->AddTag ('value');
    }





    ////
    //! Function to add a 'singleValue' tag
    // PRE  : 
    // POST : 'singleValue' tag is added to the xml output string
    function AddSingleValue ($value) {
        switch ( gettype ($value) ) {
            case 'boolean' : 
                $this->AddTag ('boolean', ($value?'1':'0')); 
                $this->CloseTag (); # close tag boolean
            break;
            case 'integer' : 
                $this->AddTag ('int', (!$value?'0':$value)); 
                $this->CloseTag (); # close tag int
            break;
            case 'double'  : 
                $this->AddTag ('double', (!$value?'0':$value)); 
                $this->CloseTag (); # close tag double
            break;
            case 'string'  : 
                $this->AddTag ('string', utf8_encode ( htmlspecialchars ($value) )); 
                $this->CloseTag (); # close tag string
            break;
            default : 
                $this->AddTag ('string', utf8_encode ( htmlspecialchars ($value) )); 
                $this->CloseTag (); # close tag string
            break;
        }
    }





    ////
    //! Function to add a 'singleValue' tag
    // PRE  : 
    // POST : 'singleValue' tag is added to the xml output string
    function AddSingleValueType ($value, $type) {
        switch ($type) {
            case 'i4'               : $this->AddTag ('i4', (!$value?'0':$value)); $this->CloseTag (); break;
            case 'int'              : $this->AddTag ('int', (!$value?'0':$value)); $this->CloseTag (); break;
            case 'boolean'          : $this->AddTag ('boolean', ($value?'1':'0')); $this->CloseTag (); break;
            case 'double'           : $this->AddTag ('double', (!$value?'0':$value)); $this->CloseTag (); break;
            case 'datetime.iso8601' :
                if ( !preg_match ('/^\d{8}T\d{2}:\d{2}:\d{2}$/', $value) )
                    trigger_error ("DateTime value hasn't got the right format (YYYYMMDDTHH:MM:SS).\n", E_USER_ERROR);

                    $this->AddTag ('datetime.iso8601', utf8_encode ( htmlspecialchars ($value) ) ); $this->CloseTag ();
                break;
            case 'base64'           : $this->AddTag ('base64', base64_encode ($value)); $this->CloseTag (); break;
            default : $this->AddTag ('string', utf8_encode ( htmlspecialchars ($value) )); $this->CloseTag (); break;
        }
    }
}

////
//! Class that reads out an XMLRPC message
// PRE  : 
// POST : 
class SimpleXMLRPCReadMessage {

    var $parser;
    var $responseArray;

    var $getCDATA;

    var $refStack;
    var $fillValue;

    var $substruct;
    var $boolBase64;
    var $cdata;
    var $xml;



    ////
    //! Constructor
    // PRE  : 
    // POST : Class members are initialized
    function SimpleXMLRPCReadMessage () {
        $this->responseArray = Array ();

        $this->refStack      = Array ( Array (NULL, 'singleData') );
        $this->fillValue     = NULL;
        $this->substruct     = 0;

        $this->boolBase64    = 0;
        $this->cdata         = '';

        $this->parser = xml_parser_create ();
        xml_set_object ($this->parser, $this);
    	xml_parser_set_option ($this->parser, XML_OPTION_CASE_FOLDING, true);
    	xml_parser_set_option ($this->parser, XML_OPTION_SKIP_WHITE, true);
        xml_set_element_handler ($this->parser, "tag_open", "tag_close");
        xml_set_character_data_handler ($this->parser, "cdata");
    }





    ////
    //! Function that parses the given XMLRPC message
    // PRE  : 
    // POST : 
    function parse ($data) { 
        xml_parse ($this->parser, $data);
    }





    ////
    //! Function that handles the begin (open) tag when parsing XML
    // PRE  : 
    // POST : 
    function tag_open ($parser, $tag, $attributes) { 
        switch ($tag) {
            case 'METHODNAME':
                $ref = &$this->responseArray;
                $this->fillValue = &$ref[]['methodName'];

                # Unshift reference on the refStack
                Array_unshift ($this->refStack, Array (&$ref, 'methodName'));
                $this->getCDATA = true;
                break;
            case 'VALUE' :
                if ($this->refStack[0][1] == 'singleData') {
                    # Unshift reference on the refStack
                    Array_unshift ($this->refStack, Array (&$this->responseArray, 'singleData'));
                }
                break;
            case 'STRUCT' :
                # Get first element from the refStack
                $refTopOfStack = &$this->refStack[0];

                $keys = NULL;
                # Check if the previous tag was a struct
                if ( is_array ($refTopOfStack[0]) ) {
                    $keys = array_keys ($refTopOfStack[0]);

                    # Check if we're dealing with an 'normal' array or an associative array
                    # Because an array is actually a map in PHP all arrays are associative
                    # To make a distinction between a normally called 'normal' array and an
                    # associative array I check if all the keys are of type integer and are 
                    # increasing in value with 1. If not then it's assumed that we're dealing
                    # with an associative array
                    # THE 'NORMAL' ARRAY HAS TO BE FRESH (NOT MODIFIED) OTHERWISE THE INDICES
                    # MIGHT NOT BE INCREASING WITH 1
                    $is_hash = false;

                    for ($i = 0; $i < count ($keys); $i++ ) {
                        if ( gettype ($keys[$i]) == 'string' or $i != $keys[$i]) {
                            $is_hash = true;
                            break 1;
                        }
                    }
                    # ##################################################################### #
                }

                # Create a new reference that's referencing to a new made array
                # if we didn't get any keys back
                # In case the previous tag was an array we also have to set the
                # reference to a new made array
                # In case the previous tag was a struct the reference has to be
                # refering to the key of the struct and not to a new array
                if ( count($keys) == 0)
                    $ref = &$refTopOfStack[0][];
                elseif (!$is_hash)
                    $ref = &$refTopOfStack[0][];
                else
                    $ref = &$refTopOfStack[0][$keys[count($keys) - 1]];

                # Unshift the new reference on the stack
                Array_unshift ($this->refStack, Array (&$ref, 'array'));

                $this->substruct++;
                break;
            case 'MEMBER' :
                # Bovenste Array van de stack halen
                $refTopOfStack = &$this->refStack[0];

                # De nieuwe reference die we gaat gebruiken bij het verwerken van de struct
                # verwijst naar de ref die is aangemaakt bij de value-tag
                $ref = &$refTopOfStack[0];

                # Unshift de nieuwe reference op de stack zodat ie wordt gebruikt
                Array_unshift ($this->refStack, Array (&$ref, 'structKey'));
                break;
            case 'ARRAY' :
                # Get the reference of the value tag of the stack
                $refTopOfStack = &$this->refStack[0];

                $keys = NULL;
                # Check if the previous tag was a struct
                if ( is_array ($refTopOfStack[0]) )
                    $keys = array_keys ($refTopOfStack[0]);

                # Create a new reference that's referencing to a new made array
                # if we didn't get any keys back
                # In case the previous tag was an array we also have to set the
                # reference to a new made array
                # In case the previous tag was a struct the reference has to be
                # refering to the key of the struct and not to a new array
                if ( count($keys) == 0)
                    $ref = &$refTopOfStack[0][];
                elseif ( gettype ($keys[ count ($keys) - 1]) == 'integer' )
                    $ref = &$refTopOfStack[0][];
                else
                    $ref = &$refTopOfStack[0][$keys[count($keys) - 1]];

                # Unshift the new reference on the stack
                Array_unshift ($this->refStack, Array (&$ref, 'array'));
                break;
            case 'NAME' :
                $this->getCDATA = true;
                break;
            case 'I4':
            case 'INT':
            case 'BOOLEAN':
            case 'STRING':
            case 'DOUBLE':
            case 'DATETIME.ISO8601':
                $this->getCDATA = true;
                break;
            case 'BASE64':
                $this->boolBase64 = 1;
                $this->getCDATA = true;
                break;
        }
        
        if ($tag == 'value');
    }





    ////
    //! Function that sets the received CDATA
    // PRE  : 
    // POST : 
    function setCDATA ($cdata) {
        $refArray = &$this->refStack[0];
        switch ($refArray[1]) {
            case 'singleData'  :
            case 'array'       :
                if ($this->boolBase64) {
                    $refArray[0][] = base64_decode ($cdata);
                    $this->boolBase64 = 0;
                } else {
                    $refArray[0][] = utf8_decode ($cdata);
                }
                break;
            case 'methodName'  :
                $this->fillValue = utf8_decode ($cdata);
                break;
            case 'structKey'   :
                $this->fillValue = &$refArray[0][$cdata];
                $refArray[1] = 'structValue';
                break;
            case 'structValue' :
                $this->fillValue = utf8_decode ($cdata);
                $refArray[1] = 'structKey';
                break;
        }
    }





    ////
    //! Function that handles the cdata when parsing XML
    // PRE  : 
    // POST : 
    function cdata ($parser, $cdata) {
        if ($this->getCDATA) {
            $this->cdata .= $cdata;
        }
    }





    ////
    //! Function that handles the end tag when parsing XML
    // PRE  : 
    // POST : 
    function tag_close ($parser, $tag) {
        $this->cdata != '' ? $this->setCDATA ($this->cdata) : FALSE;
        $this->cdata = '';
        $this->getCDATA = false;

        switch ($tag) {
            case 'VALUE' :
                if ($this->refStack[0][1] == 'singleData') {
                    array_shift ($this->refStack);
                }
                break;
            case 'STRUCT' :
                $this->substruct--;
                array_shift ($this->refStack);
                break;
            case 'MEMBER' :
                array_shift ($this->refStack);
                break;
            case 'ARRAY' :
                array_shift ($this->refStack);
                break;
            case 'METHODNAME' :
                array_shift ($this->refStack);
                break;
        }
    }
}

////
//! Class that serves as an XMLRPC client
// PRE  : 
// POST : 
class SimpleXMLRPCClient {

    var $server2Send2;
    var $xmlrpcMessage;

    var $debug;

    var $logging;



    ////
    //! Constructor
    // PRE  : 
    // POST : Class members are initialized
    function SimpleXMLRPCClient () {
        $this->debug         = 0;
        $this->logging       = false;

        $this->server2Send2  = Array ();
        $this->xmlrpcMessage = new SimpleXMLRPCMakeMessage ();
    }





    ////
    //! Function to set the XMLRPC server to send the message to
    // PRE  : 
    // POST : 
    function SetXMLRPCServer ($hostname, $path, $port = 0, $boolHttps = 0) {
        $this->server2Send2 = array ('hostname' => $hostname,
                                     'path'     => $path,
                                     'port'     => $port,
                                     'https'    => $boolHttps);
    }





    ////
    //! Function that 'creates' an XMLRPC message
    // PRE  : The methodName of the method that the XMLRPC message wants to call
    // POST : XMLRPC message header is set
    function CreateXMLRPCMessage ($method) {
        $this->xmlrpcMessage->CreateXMLRPCRequestMessage ($method);
    }





    ////
    //! Function that sets the method that the XMLRPC call wants to call
    // PRE  : the name of the method to be set 
    // POST : member variable methodName is set
    function AddArray ($array) {
        $this->xmlrpcMessage->AddArray ($array);
    }





    ////
    //! Function that sets the method that the XMLRPC call wants to call
    // PRE  : the name of the method to be set 
    // POST : member variable methodName is set
    function AddValue ($value, $type = '') {
        $this->xmlrpcMessage->AddValue ($value, $type);
    }





    ////
    //! Function that returns the RAW XML of the XMLRPC message
    // PRE  : 
    // POST : returns a string with the XML message
    function ShowRawXMLRPCMessage () {
        return $this->xmlrpcMessage->GetMessageXML ();
    }





    ////
    //! Function to send the XMLRPC message
    // PRE  : 
    // POST : Return a hash with the response values
    function SendXMLRPC () {
        if (count ($this->server2Send2) == 0) {
            print "There isn't specified what server to send to.\n";
            exit();
        }

        # Create URL to send to
        $this->server2Send2['https'] ? $url = 'https://' : $url = 'http://';

        $url .= $this->server2Send2['hostname'];
        $this->server2Send2['port'] > 0 ? $url .= ':'.$this->server2Send2['port'] : FALSE;
        $url .= $this->server2Send2['path'];

        $this->debug ? print $url."\n" : FALSE;

        $ch = curl_init();

        # Remove/Replace headers that we don't want
        # Standard CURL sets the Content-type to 'application/x-www-form-urlencoded' when a Post request is done
        # We don't want that so we set it to 'text/xml'.
        curl_setopt ($ch, CURLOPT_HTTPHEADER, Array ("Content-Type: text/xml"));

        # Show verbose (yes/no)
        $this->debug ? curl_setopt ($ch, CURLOPT_VERBOSE, 1) : FALSE;
        curl_setopt ($ch, CURLOPT_URL, $url);
        # Also give back the headers
        $this->debug == 2 ? curl_setopt ($ch, CURLOPT_HEADER, 1) : FALSE;
        curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt ($ch, CURLOPT_POST, 1);
        $xml = &$this->xmlrpcMessage->GetMessageXML ();
        curl_setopt ($ch, CURLOPT_POSTFIELDS, $xml);

		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,false);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,false);
        $this->debug ? print "\n\n^^^^^^\n$xml\n^^^^^^\n\n" : FALSE;

        $result = curl_exec ($ch);

        $curlError = &curl_error ($ch);
        if ($curlError == '') {
            $httpCode = curl_getinfo ($ch, CURLINFO_HTTP_CODE);
            
            if ( $httpCode == 200) {
                if ($result == '') {
                    $returnValue[0] = Array (
                        'faultCode'   => 4,
                        'faultString' => "No data received from server."
                    );

                    if ($this->logging) {
                        $log->LogReceivedData ("faultCode   : ".$returnValue[0]['faultCode']."\nfaultString : ".$returnValue[0]['faultString']);
                    }
                } else {
                    if ($this->logging) {
                        $log->LogReceivedData ($result);
                    }

                    $xml_parser = new simpleXMLRPCReadMessage ();
                    $xml_parser->parse ($result);

                    $returnValue = $xml_parser->responseArray;
                }
            } else {
                $description = $this->GetHTTPCodeDescription ($httpCode);
                $returnValue[0] = Array (
                    'faultCode'   => 5,
                    'faultString' => "Didn't receive 200 OK from remote server. (Received '$httpCode $description' on `$url`)"
                );

                if ($this->logging) {
                    $log->LogReceivedData ("faultCode   : ".$returnValue[0]['faultCode']."\nfaultString : ".$returnValue[0]['faultString']);
                }
            }
        } else {
            $returnValue[0] = Array (
                'faultCode'   => 6,
                'faultString' => "CURL error. ('$curlError' on `$url`)"
            );

            if ($this->logging) {
                $log->LogReceivedData ("faultCode   : ".$returnValue[0]['faultCode']."\nfaultString : ".$returnValue[0]['faultString']);
            }
        }

        curl_close ($ch);

        return $returnValue;
    }





    ////
    //! Function to return the description of an HTTP code
    // PRE  : the HTTP code
    // POST : The description is returned
    function GetHTTPCodeDescription ($httpCode) {
        $httpCodeDescription = Array (
            '100' => 'Continue',
            '101' => 'Switching Protocols',
            '200' => 'OK',
            '201' => 'Created',
            '202' => 'Accepted',
            '203' => 'Non-Authoritative Information',
            '204' => 'No Content',
            '205' => 'Reset Content',
            '206' => 'Partial Content',
            '300' => 'Multiple Choices',
            '301' => 'Moved Permanently',
            '302' => 'Moved Temporarily',
            '303' => 'See Other',
            '304' => 'Not Modified',
            '305' => 'Use Proxy',
            '400' => 'Bad Request',
            '401' => 'Unauthorized',
            '402' => 'Payment Required',
            '403' => 'Forbidden',
            '404' => 'Not Found',
            '405' => 'Method Not Allowed',
            '406' => 'Not Acceptable',
            '407' => 'Proxy Authentication Required',
            '408' => 'Request Time-out',
            '409' => 'Conflict',
            '410' => 'Gone',
            '411' => 'Length Required',
            '412' => 'Precondition Failed',
            '413' => 'Request Entity Too Large',
            '414' => 'Request-URI Too Large',
            '415' => 'Unsupported Media Type',
            '500' => 'Internal Server Error',
            '501' => 'Not Implemented',
            '502' => 'Bad Gateway',
            '503' => 'Service Unavailable',
            '504' => 'Gateway Time-out',
            '505' => 'HTTP Version not supported'
        );

        return $httpCodeDescription[$httpCode];
    }
}

?>